#include "Board.h"

void CBoard::Init (char *map, int size)
{
	//Set default value for variables
	m_size = size;
	m_key_status = 'x';

	m_current_position.x = 0;
	m_current_position.y = 0;

	m_is_completed = false;

	//Allocate new map
	m_map = new CCell*[m_size];
	for (int i = 0; i < m_size; i++)
	{
		m_map[i] = new CCell[m_size];
	}

	//Get data from 1-dimension character array
	for (int i = 0; i < m_size * m_size; i++)
	{
		m_map[i / m_size][i % m_size].SetType (map[i]);
	}

	//Calculate number of colours
	m_number_of_line = 0;
	for (int i = 0; i < m_size; i++)
	{
		for (int j = 0; j < m_size; j++)
		{
			if (m_map[i][j].GetType () < 'a')
			{
				m_number_of_line++;
			}
		}
	}
	m_number_of_line /= 2;

	//Allocate lines for every colours
	m_line = new CLine[m_number_of_line];
}

void CBoard::Release ()
{
	for (int i = 0; i < m_size; i++)
	{
		delete[] m_map[i];
	}
	delete[] m_map;
	m_map = NULL;

	delete[] m_line;
	m_line = NULL;
}

void CBoard::CheckKeyUp ()
{
	if (GIsKeyPress (KEY_UP))
	{
		if (m_current_position.y > 0)
		{
			char current_cell_type = m_map[m_current_position.x][m_current_position.y - 1].GetType ();
			//Lower-case character
			if (current_cell_type >= 'a' || m_key_status == 'x')
			{
				m_current_position.y--;
			}
			//Point with same colour to the current line
			else if (current_cell_type + 32 == m_key_status)
			{
				m_current_position.y--;

				//If current position's already the first point of line, clear line
				if (PointCompare (m_line[GetLine (m_key_status)].GetListPoint ().front (), m_current_position))
				{
					ClearLine (m_current_position);
				}
				//Add this point to the back of line (line is completed)
				else
				{
					m_line[GetLine (m_key_status)].PushBack (m_current_position);
				}
				m_key_status = 'x';
			}
		}
	}
}

void CBoard::CheckKeyDown ()
{
	if (GIsKeyPress (KEY_DOWN))
	{
		if (m_current_position.y < m_size - 1)
		{
			char current_cell_type = m_map[m_current_position.x][m_current_position.y + 1].GetType ();
			if (current_cell_type >= 'a' || m_key_status == 'x')
			{
				m_current_position.y++;
			}
			else if (current_cell_type + 32 == m_key_status)
			{
				m_current_position.y++;
				if (PointCompare (m_line[GetLine (m_key_status)].GetListPoint ().front (), m_current_position))
				{
					ClearLine (m_current_position);
				}
				else
				{
					m_line[GetLine (m_key_status)].PushBack (m_current_position);
				}
				m_key_status = 'x';
			}
		}
	}
}

void CBoard::CheckKeyLeft ()
{
	if (GIsKeyPress (KEY_LEFT))
	{
		if (m_current_position.x > 0)
		{
			char current_cell_type = m_map[m_current_position.x - 1][m_current_position.y].GetType ();
			if (current_cell_type >= 'a' || m_key_status == 'x')
			{
				m_current_position.x--;
			}
			else if (current_cell_type + 32 == m_key_status)
			{
				m_current_position.x--;
				if (PointCompare (m_line[GetLine (m_key_status)].GetListPoint ().front (), m_current_position))
				{
					ClearLine (m_current_position);
				}
				else
				{
					m_line[GetLine (m_key_status)].PushBack (m_current_position);
				}
				m_key_status = 'x';
			}
		}
	}
}

void CBoard::CheckKeyRight ()
{
	if (GIsKeyPress (KEY_RIGHT))
	{
		if (m_current_position.x < m_size - 1)
		{
			char current_cell_type = m_map[m_current_position.x + 1][m_current_position.y].GetType ();
			if (current_cell_type >= 'a' || m_key_status == 'x')
			{
				m_current_position.x++;
			}
			else if (current_cell_type + 32 == m_key_status)
			{
				m_current_position.x++;
				if (PointCompare (m_line[GetLine (m_key_status)].GetListPoint ().front (), m_current_position))
				{
					ClearLine (m_current_position);
				}
				else
				{
					m_line[GetLine (m_key_status)].PushBack (m_current_position);
				}
				m_key_status = 'x';
			}
		}
	}
}

void CBoard::CheckOtherKey ()
{
	if (GIsKeyPress (KEY_SPACE))
	{
		//Get character in the current position
		m_key_status = GetCell (m_current_position).GetType ();

		if (m_key_status < 'a')
		{
			for (int i = 0; i < m_size; i++)
			{
				//Already have line in this colour
				if (m_line[i].GetType () == m_key_status)
				{
					//Clear line
					if (m_line[i].GetListPoint ().size () != 0)
					{
						//Clear line only if line do not complete.
						if (GetCell (m_line[i].GetListPoint ().back ()).GetType () >= 'a')
						{
							ClearLine (m_line[i].GetListPoint ().front ());

							//Add point
							m_line[i].PushBack (m_current_position);
							m_map[m_current_position.x][m_current_position.y].SetType (m_key_status);
							break;
						}
					}
				}
				//Create first point for new line
				else if (m_line[i].GetListPoint ().size () == 0)
				{
					m_line[i].PushBack (m_current_position);
					m_line[i].SetType (m_key_status);
					GetCell (m_current_position).SetType (m_key_status);

					break;
				}
			}
		}

		m_key_status = tolower (m_key_status);
	}

	//Return to default value
	if (GIsKeyPress (KEY_ESC))
	{
		m_key_status = 'x';
	}
}

//Get the index of line
int CBoard::GetLine (char type)
{
	for (int i = 0; i < m_size; i++)
	{
		if (m_line[i].GetType () == toupper (type))
		{
			return i;
		}
	}

	return -1;
}

void CBoard::ClearLine (GPoint2D point)
{
	int i = GetLine (GetCell (point).GetType ());
	
	//Clear line from back to current position
	while (!PointCompare (m_line[i].GetListPoint ().back (), point))
	{
		//Keep cell's type if it's back of the line and it is a point (line is completed)
		if (GetCell (m_line[i].GetListPoint ().back ()).GetType() >= 'a')
		{
			m_map[m_line[i].GetListPoint ().back ().x][m_line[i].GetListPoint ().back ().y].SetType ('x');
		}
		m_line[i].PopBack ();
	}

	//Pop at point's position
	if (!PointCompare (m_line[i].GetListPoint ().front (), point))
	{
		m_map[point.x][point.y].SetType ('x');
	}
	m_line[i].PopBack ();
}

CCell CBoard::GetCell (GPoint2D point)
{
	return m_map[point.x][point.y];
}

//Check if 2 points is the same or not
bool CBoard::PointCompare (GPoint2D point1, GPoint2D point2)
{
	return (point1.x == point2.x && point1.y == point2.y);
}

void CBoard::UpdateBoard ()
{
	if (m_key_status != 'x')
	{
		if (GetCell (m_current_position).GetType () == 'x')
		{
			//Push current position to line
			m_line[GetLine (m_key_status)].PushBack (m_current_position);
			m_map[m_current_position.x][m_current_position.y].SetType (m_key_status);
		}
		else if (GetCell (m_current_position).GetType () >= 'a')
		{
			//Clear line have current position
			ClearLine (m_current_position);

			//Add current position to line have "m_key_status" type
			if (m_line[GetLine (m_key_status)].GetListPoint ().size () != 0)
			{
				m_map[m_current_position.x][m_current_position.y].SetType (m_key_status);
			}

			m_line[GetLine (m_key_status)].PushBack (m_current_position);
		}
	}
}

bool CBoard::IsWin ()
{
	int complete_cell = 0;

	//Calculate all length of lines
	for (int i = 0; i < m_size; i++)
	{
		complete_cell += m_line[i].GetListPoint ().size ();
	}

	if (complete_cell == m_size * m_size)
	{
		return true;
	}
	
	return false;
}

bool CBoard::IsComplete ()
{
	return m_is_completed;
}

void CBoard::Update ()
{
	//Handle Input
	CheckKeyUp ();
	CheckKeyDown ();
	CheckKeyLeft ();
	CheckKeyRight ();
	CheckOtherKey ();

	UpdateBoard ();

	if (IsWin ())
	{
		m_is_completed = true;
	}
}

void CBoard::Draw ()
{
	//Draw board
	GRectangle rect;
	rect.x = 9;
	rect.y = 10;
	rect.width = 19;
	rect.height = 19;

	GDrawRect (rect, GColors::GREEN);

	char symbol;
	//Draw map
	for (int i = 0; i < m_size; i++)
	{
		for (int j = 0; j < m_size; j++)
		{
			if (m_map[i][j].GetType () < 'a')
			{
				symbol = 2;
			}
			else if (m_map[i][j].GetType () != 'x')
			{
				symbol = 43;
			}
			else
			{
				symbol = 32;
			}

			GPutCharToBuffer (symbol, 11 + i * 3, 12 + j * 3, m_map[i][j].GetColor ());
		}
	}

	//Draw cursor
	GPutCharToBuffer (218, m_current_position.x * 3 + 10, m_current_position.y * 3 + 11);
	GPutCharToBuffer (191, m_current_position.x * 3 + 12, m_current_position.y * 3 + 11);
	GPutCharToBuffer (192, m_current_position.x * 3 + 10, m_current_position.y * 3 + 13);
	GPutCharToBuffer (217, m_current_position.x * 3 + 12, m_current_position.y * 3 + 13);
}